Authors: J. Coelho, S. Ammer
library(Momocs) # package for outline and curve analysis
setwd("/Users/del/ACADEMY/WIP/HumerusProject/Photos_final/") # location of my data
rawLPF <- import_tps(tps.path = "LPF/LPF.TPS", curves = T) # read data Left, Posterior, Females
rawLPM <- import_tps(tps.path = "LPM/LPM.TPS", curves = T) # read data Left, Posterior, Males
Rebuild dataset
For some reason, I can’t use Momocs::Out() directly on my imported TPS. Tried many ways, always obtaining different errors. I thought this migh be because the p of coordinates is not consistent. So I resample them. In my understanding coo_sample() works well for open curves, while I prefered coo_interpolate() for my closed outlines, sinde they had very inconsistent p.
# Rebuild datasets
# For females
for (i in seq_along(rawLPF$cur)){
# open (curves)
rawLPF$cur[[i]][[1]] <- coo_sample(rawLPF$cur[[i]][[1]], n = 16)
# closed (outlines)
rawLPF$cur[[i]][[2]] <- coo_interpolate(rawLPF$cur[[i]][[2]], n = 24)
# keep the IDs
names(rawLPF$cur)[[i]] <- names(rawLPF$cur)[[i]]
}
## For males
for (i in seq_along(rawLPM$cur)){
# open (curves)
rawLPM$cur[[i]][[1]] <- coo_sample(rawLPM$cur[[i]][[1]], n = 16)
# closed (outlines)
rawLPM$cur[[i]][[2]] <- coo_interpolate(rawLPM$cur[[i]][[2]], n = 24)
# keep the IDs
names(rawLPM$cur)[[i]] <- names(rawLPM$cur)[[i]]
}
Spliting data
I tried to work with my curves together but for some reason, I also got errors trying to Out() these. So first I will split my open curve (TE: Throclear Extension) from my closed outlines (OF: Olecranon Fossa), but combine my males and females datasets to see the differences among these groups.
TE.f <- list()
for (i in seq_along(rawLPF$cur)){
TE.f[[i]] <- rawLPF$cur[[i]][[1]]
names(TE.f)[[i]] <- names(rawLPF$cur)[[i]]
}
TE.m <- list()
for (i in seq_along(rawLPM$cur)){
TE.m[[i]] <- rawLPM$cur[[i]][[1]]
names(TE.m)[[i]] <- names(rawLPM$cur)[[i]]
}
Sex <- rep("Female", length(TE.f))
TE.f <- Out(TE.f, fac = as.data.frame(Sex))
Sex <- rep("Male", length(TE.m))
TE.m <- Out(TE.m, fac = as.data.frame(Sex))
TE <- combine(TE.f, TE.m)
###
OF.f <- list()
for (i in seq_along(rawLPF$cur)){
OF.f[[i]] <- rawLPF$cur[[i]][[2]]
names(OF.f)[[i]] <- names(rawLPF$cur)[[i]]
}
OF.m <- list()
for (i in seq_along(rawLPM$cur)){
OF.m[[i]] <- rawLPM$cur[[i]][[2]]
names(OF.m)[[i]] <- names(rawLPM$cur)[[i]]
}
Sex <- rep("Female", length(OF.f))
OF.f <- Out(OF.f, fac = as.data.frame(Sex))
Sex <- rep("Male", length(OF.m))
OF.m <- Out(OF.m, fac = as.data.frame(Sex))
OF <- combine(OF.f, OF.m)
Procrustes and Rotation
My pics were (anatomically) upside-down. So I use the very useful coo_rotate() by Pi radians (180º). I have done some landmarks-based analysis before were procrustes was a mandatory step. I am not sure if it is with open curves and closed outlines, however I am also doing a superimposition step here.
TE.aligned <- TE %>% coo_rotate(.,theta = pi) %>% fgProcrustes() %>% Opn()
OF.aligned <- OF %>% coo_rotate(.,theta = pi) %>% fgProcrustes() %>% coo_close()
Throclear Extension
TE.aligned %>% stack(., title = "Trochlear Extension (H. sapiens)", fac = "Sex")

TE.aligned %>% panel(., fac = "Sex")

TE.aligned %>% npoly() %>% PCA() %>% plot(.,"Sex") # I need to read about this
'nb.pts' missing and set to: 16
'degree' missing and set to: 5

TE.aligned %>% opoly() %>% PCA() %>% plot(.,"Sex") # read about this
'nb.pts' missing and set to 16
'degree' missing and set to 5

TE.aligned %>% dfourier() %>% PCA() %>% plot(.,"Sex") # strange results; might not be proper for this dataset.
'nb.h' not provided and set to 12
==========================================================================================

TE.n <- npoly(TE.aligned, nb.pts = 16, degree = 5)
TE.pca <- PCA(TE.n)
TE.pca %>% as_df() %>% ggplot() +
aes(x = PC1, y = PC2, col = Sex) + coord_equal() +
geom_point() + geom_density2d() + theme_light()

# mean shape:
TE.n %>% mshapes() %>% coo_plot()
no 'fac' provided, returns meanshape

# mean shape per group:
TE.ms <- mshapes(TE.n, 1)
TEfffs <- TE.ms$shp$Female %T>% coo_plot(border = "red")
TEmmms <- TE.ms$shp$Male %T>% coo_draw(border = "blue")
legend("topright", lwd = 1, col = c("red", "blue"), legend = c("Female", "Male"), cex = 0.8)
title(main = "Throclear Extension - Mean shapes")

# TPS plots
coo_lolli(TEfffs, TEmmms); title("Deformations")

coo_arrows(TEfffs, TEmmms); title("Deformations")

tps_grid(TEfffs, TEmmms)

tps_arr(TEfffs, TEmmms)

tps_iso(TEfffs, TEmmms)

Olecranon Fossa
OF.aligned %>% stack(.,title = "Olecranon Fossa (H. sapiens)", fac = "Sex")

OF.aligned %>% panel(., fac = "Sex")

OF.aligned %>% efourier(norm=TRUE) %>% PCA() %>% plot(.,"Sex")
'nb.h' not provided and set to 6 (99% harmonic power)

OF.f <- efourier(OF.aligned, nb.h = 6)
OF.pca <- PCA(OF.f)
OF.pca %>% as_df() %>% ggplot() +
aes(x = PC1, y = PC2, col = Sex) + coord_equal() +
geom_point() + geom_density2d() + theme_light()

# mean shape:
OF.f %>% mshapes() %>% coo_plot()
no 'fac' provided, returns meanshape

# mean shape per group:
OF.ms <- mshapes(OF.f, 1)
OFfffs <- OF.ms$shp$Female %T>% coo_plot(border = "red")
OFmmms <- OF.ms$shp$Male %T>% coo_draw(border = "blue")
legend("topright", lwd = 1, col = c("red", "blue"), legend = c("Female", "Male"), cex = 0.8)
title(main = "Olecranon Fossa - Mean shapes")

# TPS plots
coo_lolli(OFfffs, OFmmms); title("Deformations")

coo_arrows(OFfffs, OFmmms); title("Deformations")

tps_grid(OFfffs, OFmmms)

tps_arr(OFfffs, OFmmms)

tps_iso(OFfffs, OFmmms)

Individuals by individual
Throclear Extension
p <- list()
for (i in seq_along(TE.aligned)){
p[[i]] <- coo_oscillo(TE.aligned$coo[[i]])
}























































































































































Olecranon Fossa
p <- list()
for (i in seq_along(OF.aligned)){
p[[i]] <- coo_oscillo(OF.aligned$coo[[i]])
}























































































































































LS0tCnRpdGxlOiAiRGlzdGFsIEh1bWVyaSBTaGFwZSBBbmFseXNpcyBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKKkF1dGhvcnM6KiBKLiBDb2VsaG8sIFMuIEFtbWVyCgpgYGB7ciBpbXBvcnRpbmcgZGF0YX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KE1vbW9jcykgIyBwYWNrYWdlIGZvciBvdXRsaW5lIGFuZCBjdXJ2ZSBhbmFseXNpcwpzZXR3ZCgiL1VzZXJzL2RlbC9BQ0FERU1ZL1dJUC9IdW1lcnVzUHJvamVjdC9QaG90b3NfZmluYWwvIikgIyBsb2NhdGlvbiBvZiBteSBkYXRhCgpyYXdMUEYgPC0gaW1wb3J0X3Rwcyh0cHMucGF0aCA9ICJMUEYvTFBGLlRQUyIsIGN1cnZlcyA9IFQpICMgcmVhZCBkYXRhIExlZnQsIFBvc3RlcmlvciwgRmVtYWxlcwpyYXdMUE0gPC0gaW1wb3J0X3Rwcyh0cHMucGF0aCA9ICJMUE0vTFBNLlRQUyIsIGN1cnZlcyA9IFQpICMgcmVhZCBkYXRhIExlZnQsIFBvc3RlcmlvciwgTWFsZXMKCmBgYAoKIyMgUmVidWlsZCBkYXRhc2V0CgpGb3Igc29tZSByZWFzb24sIEkgY2FuJ3QgdXNlIGBNb21vY3M6Ok91dCgpYCBkaXJlY3RseSBvbiBteSBpbXBvcnRlZCBUUFMuIFRyaWVkIG1hbnkgd2F5cywgYWx3YXlzIG9idGFpbmluZyBkaWZmZXJlbnQgZXJyb3JzLiBJIHRob3VnaHQgdGhpcyBtaWdoIGJlIGJlY2F1c2UgdGhlICoqcCoqIG9mIGNvb3JkaW5hdGVzIGlzIG5vdCBjb25zaXN0ZW50LiBTbyBJIHJlc2FtcGxlIHRoZW0uIEluIG15IHVuZGVyc3RhbmRpbmcgYGNvb19zYW1wbGUoKWAgd29ya3Mgd2VsbCBmb3Igb3BlbiBjdXJ2ZXMsIHdoaWxlIEkgcHJlZmVyZWQgYGNvb19pbnRlcnBvbGF0ZSgpYCBmb3IgbXkgY2xvc2VkIG91dGxpbmVzLCBzaW5kZSB0aGV5IGhhZCB2ZXJ5IGluY29uc2lzdGVudCAqKnAqKi4KCmBgYHtyIHJlYnVpbGR9CiMgUmVidWlsZCBkYXRhc2V0cwojIEZvciBmZW1hbGVzCgpmb3IJKGkgaW4gc2VxX2Fsb25nKHJhd0xQRiRjdXIpKXsKCSMgb3BlbiAoY3VydmVzKQoJcmF3TFBGJGN1cltbaV1dW1sxXV0gPC0gY29vX3NhbXBsZShyYXdMUEYkY3VyW1tpXV1bWzFdXSwgbiA9IDE2KQoJIyBjbG9zZWQgKG91dGxpbmVzKQoJcmF3TFBGJGN1cltbaV1dW1syXV0gPC0gY29vX2ludGVycG9sYXRlKHJhd0xQRiRjdXJbW2ldXVtbMl1dLCBuID0gMjQpCgkjIGtlZXAgdGhlIElEcwoJbmFtZXMocmF3TFBGJGN1cilbW2ldXSA8LSBuYW1lcyhyYXdMUEYkY3VyKVtbaV1dCn0KCiMjIEZvciBtYWxlcwoKZm9yCShpIGluIHNlcV9hbG9uZyhyYXdMUE0kY3VyKSl7CgkjIG9wZW4gKGN1cnZlcykKCXJhd0xQTSRjdXJbW2ldXVtbMV1dIDwtIGNvb19zYW1wbGUocmF3TFBNJGN1cltbaV1dW1sxXV0sIG4gPSAxNikKCSMgY2xvc2VkIChvdXRsaW5lcykKCXJhd0xQTSRjdXJbW2ldXVtbMl1dIDwtIGNvb19pbnRlcnBvbGF0ZShyYXdMUE0kY3VyW1tpXV1bWzJdXSwgbiA9IDI0KQoJIyBrZWVwIHRoZSBJRHMKCW5hbWVzKHJhd0xQTSRjdXIpW1tpXV0gPC0gbmFtZXMocmF3TFBNJGN1cilbW2ldXQp9CgpgYGAKCiMjIFNwbGl0aW5nIGRhdGEKCkkgdHJpZWQgdG8gd29yayB3aXRoIG15IGN1cnZlcyB0b2dldGhlciBidXQgZm9yIHNvbWUgcmVhc29uLCBJIGFsc28gZ290IGVycm9ycyB0cnlpbmcgdG8gYE91dCgpYCB0aGVzZS4gU28gZmlyc3QgSSB3aWxsIHNwbGl0IG15IG9wZW4gY3VydmUgKFRFOiBUaHJvY2xlYXIgRXh0ZW5zaW9uKSBmcm9tIG15IGNsb3NlZCBvdXRsaW5lcyAoT0Y6IE9sZWNyYW5vbiBGb3NzYSksIGJ1dCBjb21iaW5lIG15IG1hbGVzIGFuZCBmZW1hbGVzIGRhdGFzZXRzIHRvIHNlZSB0aGUgZGlmZmVyZW5jZXMgYW1vbmcgdGhlc2UgZ3JvdXBzLgoKYGBge3Igc3BsaXR9CgpURS5mIDwtIGxpc3QoKQpmb3IJKGkgaW4gc2VxX2Fsb25nKHJhd0xQRiRjdXIpKXsKCVRFLmZbW2ldXSA8LSByYXdMUEYkY3VyW1tpXV1bWzFdXQoJbmFtZXMoVEUuZilbW2ldXSA8LSBuYW1lcyhyYXdMUEYkY3VyKVtbaV1dCn0KClRFLm0gPC0gbGlzdCgpCmZvcgkoaSBpbiBzZXFfYWxvbmcocmF3TFBNJGN1cikpewoJVEUubVtbaV1dIDwtIHJhd0xQTSRjdXJbW2ldXVtbMV1dCgluYW1lcyhURS5tKVtbaV1dIDwtIG5hbWVzKHJhd0xQTSRjdXIpW1tpXV0KfQoKU2V4IDwtIHJlcCgiRmVtYWxlIiwgbGVuZ3RoKFRFLmYpKQpURS5mIDwtIE91dChURS5mLCBmYWMgPSBhcy5kYXRhLmZyYW1lKFNleCkpClNleCA8LSByZXAoIk1hbGUiLCBsZW5ndGgoVEUubSkpClRFLm0gPC0gT3V0KFRFLm0sIGZhYyA9IGFzLmRhdGEuZnJhbWUoU2V4KSkKVEUgPC0gY29tYmluZShURS5mLCBURS5tKQoKIyMjCgpPRi5mIDwtIGxpc3QoKQpmb3IJKGkgaW4gc2VxX2Fsb25nKHJhd0xQRiRjdXIpKXsKCU9GLmZbW2ldXSA8LSByYXdMUEYkY3VyW1tpXV1bWzJdXQoJbmFtZXMoT0YuZilbW2ldXSA8LSBuYW1lcyhyYXdMUEYkY3VyKVtbaV1dCn0KCk9GLm0gPC0gbGlzdCgpCmZvcgkoaSBpbiBzZXFfYWxvbmcocmF3TFBNJGN1cikpewoJT0YubVtbaV1dIDwtIHJhd0xQTSRjdXJbW2ldXVtbMl1dCgluYW1lcyhPRi5tKVtbaV1dIDwtIG5hbWVzKHJhd0xQTSRjdXIpW1tpXV0KfQoKU2V4IDwtIHJlcCgiRmVtYWxlIiwgbGVuZ3RoKE9GLmYpKQpPRi5mIDwtIE91dChPRi5mLCBmYWMgPSBhcy5kYXRhLmZyYW1lKFNleCkpClNleCA8LSByZXAoIk1hbGUiLCBsZW5ndGgoT0YubSkpCk9GLm0gPC0gT3V0KE9GLm0sIGZhYyA9IGFzLmRhdGEuZnJhbWUoU2V4KSkKT0YgPC0gY29tYmluZShPRi5mLCBPRi5tKQoKCmBgYAoKIyMgUHJvY3J1c3RlcyBhbmQgUm90YXRpb24KCk15IHBpY3Mgd2VyZSAoYW5hdG9taWNhbGx5KSB1cHNpZGUtZG93bi4gU28gSSB1c2UgdGhlIHZlcnkgdXNlZnVsIGBjb29fcm90YXRlKClgIGJ5IFBpIHJhZGlhbnMgKDE4MMK6KS4gSSBoYXZlIGRvbmUgc29tZSBsYW5kbWFya3MtYmFzZWQgYW5hbHlzaXMgYmVmb3JlIHdlcmUgcHJvY3J1c3RlcyB3YXMgYSBtYW5kYXRvcnkgc3RlcC4gSSBhbSBub3Qgc3VyZSBpZiBpdCBpcyB3aXRoIG9wZW4gY3VydmVzIGFuZCBjbG9zZWQgb3V0bGluZXMsIGhvd2V2ZXIgSSBhbSBhbHNvIGRvaW5nIGEgc3VwZXJpbXBvc2l0aW9uIHN0ZXAgaGVyZS4KCmBgYHtyIFByb2NydXN0ZXN9ClRFLmFsaWduZWQgPC0gVEUgJT4lIGNvb19yb3RhdGUoLix0aGV0YSA9IHBpKSAlPiUgZmdQcm9jcnVzdGVzKCkgJT4lIE9wbigpCk9GLmFsaWduZWQgPC0gT0YgJT4lIGNvb19yb3RhdGUoLix0aGV0YSA9IHBpKSAlPiUgZmdQcm9jcnVzdGVzKCkgJT4lIGNvb19jbG9zZSgpCmBgYAoKIyBUaHJvY2xlYXIgRXh0ZW5zaW9uCgpgYGB7ciBURSBwbG90dGluZ30KClRFLmFsaWduZWQgJT4lIHN0YWNrKC4sIHRpdGxlID0gIlRyb2NobGVhciBFeHRlbnNpb24gKEguIHNhcGllbnMpIiwgZmFjID0gIlNleCIpCgpURS5hbGlnbmVkICU+JSBwYW5lbCguLCBmYWMgPSAiU2V4IikKClRFLmFsaWduZWQgJT4lIG5wb2x5KCkgJT4lIFBDQSgpICU+JSBwbG90KC4sIlNleCIpICMgSSBuZWVkIHRvIHJlYWQgYWJvdXQgdGhpcwpURS5hbGlnbmVkICU+JSBvcG9seSgpICU+JSBQQ0EoKSAlPiUgcGxvdCguLCJTZXgiKSAjIHJlYWQgYWJvdXQgdGhpcwpURS5hbGlnbmVkICU+JSBkZm91cmllcigpICU+JSBQQ0EoKSAlPiUgcGxvdCguLCJTZXgiKSAjIHN0cmFuZ2UgcmVzdWx0czsgbWlnaHQgbm90IGJlIHByb3BlciBmb3IgdGhpcyBkYXRhc2V0LgoKVEUubiA8LSBucG9seShURS5hbGlnbmVkLCBuYi5wdHMgPSAxNiwgZGVncmVlID0gNSkKVEUucGNhIDwtIFBDQShURS5uKQpURS5wY2EgJT4lIGFzX2RmKCkgJT4lIGdncGxvdCgpICsKCWFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2wgPSBTZXgpICsgY29vcmRfZXF1YWwoKSArIAoJZ2VvbV9wb2ludCgpICsgZ2VvbV9kZW5zaXR5MmQoKSArIHRoZW1lX2xpZ2h0KCkKCiMgbWVhbiBzaGFwZToKVEUubiAlPiUgbXNoYXBlcygpICU+JSBjb29fcGxvdCgpCiMgbWVhbiBzaGFwZSBwZXIgZ3JvdXA6ClRFLm1zIDwtIG1zaGFwZXMoVEUubiwgMSkKVEVmZmZzIDwtIFRFLm1zJHNocCRGZW1hbGUgJVQ+JSBjb29fcGxvdChib3JkZXIgPSAicmVkIikKVEVtbW1zIDwtIFRFLm1zJHNocCRNYWxlICVUPiUgY29vX2RyYXcoYm9yZGVyID0gImJsdWUiKQpsZWdlbmQoInRvcHJpZ2h0IiwgbHdkID0gMSwgY29sID0gYygicmVkIiwgImJsdWUiKSwgbGVnZW5kID0gYygiRmVtYWxlIiwgIk1hbGUiKSwgY2V4ID0gMC44KQp0aXRsZShtYWluID0gIlRocm9jbGVhciBFeHRlbnNpb24gLSBNZWFuIHNoYXBlcyIpCgojIFRQUyBwbG90cwpjb29fbG9sbGkoVEVmZmZzLCBURW1tbXMpOyB0aXRsZSgiRGVmb3JtYXRpb25zIikKY29vX2Fycm93cyhURWZmZnMsIFRFbW1tcyk7IHRpdGxlKCJEZWZvcm1hdGlvbnMiKQp0cHNfZ3JpZChURWZmZnMsIFRFbW1tcykKdHBzX2FycihURWZmZnMsIFRFbW1tcykKdHBzX2lzbyhURWZmZnMsIFRFbW1tcykKYGBgCgojIE9sZWNyYW5vbiBGb3NzYQoKYGBge3IgT0YgcGxvdHRpbmd9CgpPRi5hbGlnbmVkICU+JSBzdGFjayguLHRpdGxlID0gIk9sZWNyYW5vbiBGb3NzYSAoSC4gc2FwaWVucykiLCBmYWMgPSAiU2V4IikKT0YuYWxpZ25lZCAlPiUgcGFuZWwoLiwgZmFjID0gIlNleCIpCgpPRi5hbGlnbmVkICU+JSBlZm91cmllcihub3JtPVRSVUUpICU+JSBQQ0EoKSAlPiUgcGxvdCguLCJTZXgiKQoKT0YuZiA8LSBlZm91cmllcihPRi5hbGlnbmVkLCBuYi5oID0gNikKT0YucGNhIDwtIFBDQShPRi5mKQpPRi5wY2EgJT4lIGFzX2RmKCkgJT4lIGdncGxvdCgpICsKCWFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2wgPSBTZXgpICsgY29vcmRfZXF1YWwoKSArIAoJZ2VvbV9wb2ludCgpICsgZ2VvbV9kZW5zaXR5MmQoKSArIHRoZW1lX2xpZ2h0KCkKCiMgbWVhbiBzaGFwZToKT0YuZiAlPiUgbXNoYXBlcygpICU+JSBjb29fcGxvdCgpCiMgbWVhbiBzaGFwZSBwZXIgZ3JvdXA6Ck9GLm1zIDwtIG1zaGFwZXMoT0YuZiwgMSkKT0ZmZmZzIDwtIE9GLm1zJHNocCRGZW1hbGUgJVQ+JSBjb29fcGxvdChib3JkZXIgPSAicmVkIikKT0ZtbW1zIDwtIE9GLm1zJHNocCRNYWxlICVUPiUgY29vX2RyYXcoYm9yZGVyID0gImJsdWUiKQpsZWdlbmQoInRvcHJpZ2h0IiwgbHdkID0gMSwgY29sID0gYygicmVkIiwgImJsdWUiKSwgbGVnZW5kID0gYygiRmVtYWxlIiwgIk1hbGUiKSwgY2V4ID0gMC44KQp0aXRsZShtYWluID0gIk9sZWNyYW5vbiBGb3NzYSAtIE1lYW4gc2hhcGVzIikKCiMgVFBTIHBsb3RzCmNvb19sb2xsaShPRmZmZnMsIE9GbW1tcyk7IHRpdGxlKCJEZWZvcm1hdGlvbnMiKQpjb29fYXJyb3dzKE9GZmZmcywgT0ZtbW1zKTsgdGl0bGUoIkRlZm9ybWF0aW9ucyIpCnRwc19ncmlkKE9GZmZmcywgT0ZtbW1zKQp0cHNfYXJyKE9GZmZmcywgT0ZtbW1zKQp0cHNfaXNvKE9GZmZmcywgT0ZtbW1zKQoKYGBgCgojIEluZGl2aWR1YWxzIGJ5IGluZGl2aWR1YWwgCiMjIFRocm9jbGVhciBFeHRlbnNpb24KCmBgYHtyfQpwIDwtIGxpc3QoKQpmb3IgKGkgaW4gc2VxX2Fsb25nKFRFLmFsaWduZWQpKXsKCXBbW2ldXSA8LSBjb29fb3NjaWxsbyhURS5hbGlnbmVkJGNvb1tbaV1dKQp9CmBgYAoKIyMgT2xlY3Jhbm9uIEZvc3NhCgpgYGB7cn0KcCA8LSBsaXN0KCkKZm9yIChpIGluIHNlcV9hbG9uZyhPRi5hbGlnbmVkKSl7CglwW1tpXV0gPC0gY29vX29zY2lsbG8oT0YuYWxpZ25lZCRjb29bW2ldXSkKfQpgYGAKCg==